import os
import time
import gc
import json

# 外设控制
import board
import digitalio
import analogio

# 网络请求：
import ssl
import wifi
import socketpool
import adafruit_requests as requests

# 字体显示：
import displayio
import terminalio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.bitmap_label import Label

TOC_CONTENT_LINES = 10

KEY_LEFT = 1
KEY_MID = 2
KEY_RIGHT = 3
KEY_BOOT = 4
boot_pin = digitalio.DigitalInOut(board.BOOT)
keypad_pin = analogio.AnalogIn(board.MUTE_STATUS)

K_AUTHOR = 'author'
K_RHYTHMIC = 'rhythmic'
K_PATH = 'path'
K_PART = 'part'
K_PARAGRAPHS = 'paragraphs'

CN_FONT_WIDTH = 16
CN_FONT_HEIGHT = 16
CN_FONT_PATH = '/fonts/wenquanyi_12pt.pcf'
g_cn_font = bitmap_font.load_font(CN_FONT_PATH)


# 显示白色背景
def show_display_bg(group):
    bg_color_bitmap = displayio.Bitmap(board.DISPLAY.width, board.DISPLAY.height, 1)
    bg_color_palette = displayio.Palette(1)
    bg_color_palette[0] = 0xFFFFFF
    bg_sprite = displayio.TileGrid(bg_color_bitmap, pixel_shader=bg_color_palette, x=0, y=0)
    group.append(bg_sprite)


# 显示WiFi正在扫描：
def show_wifi_scanning(group):
    label_wifi_scanning = Label(
        font=terminalio.FONT,
        text='Wi-Fi Scanning ...',
        color=0x000000)
    dims = label_wifi_scanning.bounding_box
    label_wifi_scanning.x = (board.DISPLAY.width - dims[2]) // 2
    label_wifi_scanning.y = (board.DISPLAY.height - dims[3]) // 2
    group.append(label_wifi_scanning)


# 显示WiFi正在连接：
def show_wifi_connecting(group):
    label_wifi_connecting = Label(
        font=terminalio.FONT,
        text='Wi-Fi Connecting ...',
        color=0x000000)
    dims = label_wifi_connecting.bounding_box
    label_wifi_connecting.x = (board.DISPLAY.width - dims[2]) // 2
    label_wifi_connecting.y = (board.DISPLAY.height - dims[3]) // 2
    group.append(label_wifi_connecting)


# 显示WiFi状态：
def show_wifi_status(group):
    wifi_status_text = f'IP: {wifi.radio.ipv4_address}\n'
    wifi_status_text += f'GW: {wifi.radio.ipv4_gateway}\n'
    wifi_status_text += f'DNS: {wifi.radio.ipv4_dns}\n'
    label_wifi_status = Label(
        font=terminalio.FONT,
        text=wifi_status_text,
        color=0x000000)
    dims = label_wifi_status.bounding_box
    label_wifi_status.x = (board.DISPLAY.width - dims[2]) // 2
    label_wifi_status.y = (board.DISPLAY.height - dims[3]) // 2
    group.append(label_wifi_status)


# 打开WiFi、触发扫描
def wifi_start_scanning():
    wifi.radio.stop_station()
    wifi.radio.start_station()
    print('Wi-Fi Scanning ...')
    scan_result = wifi.radio.start_scanning_networks()
    # time.sleep(3)
    # # 打印扫描结果
    # for ap in scan_result:
    #     print(ap.ssid, ap.rssi)


# 读取WiFi热点的SSID和秘密
def wifi_load_config():
    CFG_PATH = '/cfg/wifi.json'
    wifi_cfg = {}
    with open(CFG_PATH) as f:
        wifi_cfg = json.loads(f.read())

    ssid = wifi_cfg['ssid']
    psk = wifi_cfg['psk']
    # print(AP_SSID, AP_PSK)
    return ssid, psk


# 连接WiFi热点
def wifi_connect_hostspot(ssid, psk):
    print('connect to AP ...')
    retries = 10
    for i in range(retries):
        try:
            print(f'connect {i} ...')
            wifi.radio.connect(ssid, psk)
            time.sleep(1)
            break
        except Exception as e:
            print(f'Exception when connect: {e}')

    print('start DHCP ...')
    wifi.radio.start_dhcp()
    time.sleep(1)
    print('IP:', wifi.radio.ipv4_address)
    print('GW:', wifi.radio.ipv4_gateway)
    print('DNS:', wifi.radio.ipv4_dns)


# 显示IP地址信息
def show_ip_address(group):
    label_ip_addr = Label(
        font=terminalio.FONT,
        text='Wi-Fi Connecting ...',
        color=0x000000)
    dims = label_ip_addr.bounding_box
    label_ip_addr.x = (board.DISPLAY.width - dims[2]) // 2
    label_ip_addr.y = (board.DISPLAY.height - dims[3]) // 2
    group.append(label_ip_addr)


def http_create_session():
    socket_pool = socketpool.SocketPool(wifi.radio)
    ssl_context = ssl.create_default_context()  # for https request
    http = requests.Session(socket_pool, ssl_context)
    return http


def http_get_ci_index(http):
    SONGCI_POPULAR_INDEX = 'https://gitee.com/swxu/poem/raw/master/song/ci_pop/index'
    print("Fetching JSON data from %s" % SONGCI_POPULAR_INDEX)
    response = http.get(SONGCI_POPULAR_INDEX)
    resp_text = response.text
    print("response content length: ", len(resp_text))
    response.close()
    font_load_glyphs(g_cn_font, resp_text)
    ci_index = json.loads(resp_text)
    # for ci in ci_index[:TOC_CONTENT_LINES]:
    #     print(ci['path'], ci['author'], ci['rhythmic'])
    return ci_index


def http_get_poem(http, poem_id):
    poem_url = f'https://gitee.com/swxu/poem/raw/master/song/ci_pop/{poem_id}'
    print("Fetching JSON data from %s" % poem_url)
    response = http.get(poem_url)
    resp_text = response.text
    print("response content length: ", len(resp_text))
    response.close()
    font_load_glyphs(g_cn_font, resp_text)
    poem = json.loads(resp_text)
    print(poem)
    return poem


CN_FONT_GAP = 4

TERMINAL_FONT_HEIGHT = terminalio.FONT.bitmap.height
print('terminal font height:', TERMINAL_FONT_HEIGHT)
print('board display height:', board.DISPLAY.height)
TOC_CONTENT_HEIGHT = (CN_FONT_HEIGHT + CN_FONT_GAP) * TOC_CONTENT_LINES - CN_FONT_GAP
print('index page content height:', TOC_CONTENT_HEIGHT)
TOC_CONTENT_BASE_X = 16  # aera area
TOC_CONTENT_BASE_Y = (board.DISPLAY.height - TOC_CONTENT_HEIGHT + CN_FONT_HEIGHT) // 2
print('index page base y:', TOC_CONTENT_BASE_Y)


# 绘制目录界面
def show_ci_index(group, ci_index, page_id):
    index_group = displayio.Group()
    group.append(index_group)
    # 顶部显示 Table of Content
    label_header = Label(font=terminalio.FONT, text='Table of Content', color=0)
    label_header.x = (board.DISPLAY.width - label_header.bounding_box[2]) // 2
    label_header.y = TOC_CONTENT_BASE_Y // 2 - CN_FONT_GAP
    index_group.append(label_header)
    # 底部显示 Page 1/28
    label_footer = Label(font=terminalio.FONT,
                         text=f'Page {page_id + 1}/{len(ci_index) // TOC_CONTENT_LINES}', color=0)
    label_footer.x = (board.DISPLAY.width - label_footer.bounding_box[2]) // 2
    label_footer.y = (board.DISPLAY.height + (TOC_CONTENT_BASE_Y + TOC_CONTENT_HEIGHT)) // 2
    index_group.append(label_footer)
    base_y = TOC_CONTENT_BASE_Y
    if (page_id + 1) * TOC_CONTENT_LINES > len(ci_index):
        page_id = len(ci_index) / TOC_CONTENT_LINES - 1
    # 中间区域显示诗词编号、作者、标题、首句
    for row in range(TOC_CONTENT_LINES):
        idx = page_id * TOC_CONTENT_LINES + row
        index = ci_index[idx]
        index_text_line = f'{index[K_PATH]} {index[K_AUTHOR]} {index[K_RHYTHMIC]} {index[K_PART]}\n'
        label_index_line = Label(
            font=g_cn_font,
            text=index_text_line,
            color=0x000000)
        # dims = label_index_line.bounding_box
        label_index_line.x = TOC_CONTENT_BASE_X
        label_index_line.y = base_y
        base_y += CN_FONT_HEIGHT + CN_FONT_GAP
        index_group.append(label_index_line)
    return index_group


def keypad_adc_value():
    keypad_values = []
    SAMPLE_TIMES = 5
    for i in range(SAMPLE_TIMES):
        keypad_values.append(keypad_pin.value)
        time.sleep(0.002)
    keypad_values.sort()
    return keypad_values[SAMPLE_TIMES // 2]


def key_status_read():
    key_status = {}
    key_status[KEY_BOOT] = boot_pin.value
    key_status[KEY_LEFT] = False
    key_status[KEY_MID] = False
    key_status[KEY_RIGHT] = False
    average_value = keypad_adc_value()
    if average_value < 27000:
        key_status[KEY_RIGHT] = True
    elif average_value < 43000:
        key_status[KEY_MID] = True
    elif average_value < 56000:
        key_status[KEY_LEFT] = True
    return key_status


def show_select_ui(group, page_id):
    label_cursor = Label(font=terminalio.FONT, text='=>', color=0xBF0000)
    dims = label_cursor.bounding_box
    label_cursor.x = TOC_CONTENT_BASE_X - dims[2] - 2
    label_cursor.y = TOC_CONTENT_BASE_Y
    label_cursor_base_y = label_cursor.y
    group.append(label_cursor)
    cursor_id = page_id * TOC_CONTENT_LINES
    select_id, key_pressed = 0, 0
    while True:
        gc.collect()
        key_status = key_status_read()
        if key_status[KEY_RIGHT]:
            cursor_id += 1
            if cursor_id >= (page_id + 1) * TOC_CONTENT_LINES:
                select_id, key_pressed = cursor_id, KEY_RIGHT
                break
        elif key_status[KEY_LEFT]:
            cursor_id -= 1
            if cursor_id < 0:
                cursor_id = 0
            elif cursor_id < page_id * TOC_CONTENT_LINES:
                select_id, key_pressed = cursor_id, KEY_LEFT
                break
        elif key_status[KEY_MID]:
            select_id, key_pressed = cursor_id, KEY_MID
            break
        label_cursor.y = (cursor_id % TOC_CONTENT_LINES) * (
                CN_FONT_HEIGHT + CN_FONT_GAP) + label_cursor_base_y
        time.sleep(.05)
    gc.collect()
    return select_id, key_pressed


POEM_CONTENT_SHOW_LINES = 11


def show_poem(group, poem_content, author, base_y, start_idx):
    poem_group = displayio.Group()
    group.append(poem_group)
    last_idx = 0
    for line in poem_content[start_idx: start_idx + POEM_CONTENT_SHOW_LINES]:
        color = 0x000000 if line != author else 0xA0A0A0
        # print(line, base_y, '0x%x' % color)
        label_line = Label(font=g_cn_font, text=line, color=color)
        label_width = label_line.bounding_box[2]
        label_line.x = (board.DISPLAY.width - label_width) // 2
        label_line.y = base_y
        base_y += CN_FONT_HEIGHT + CN_FONT_GAP
        poem_group.append(label_line)
        last_idx = poem_content.index(line)
    label_progress = Label(font=terminalio.FONT,
                           text=f'Lines: {last_idx + 1}/{len(poem_content)}', color=0x7F7F7F)
    label_progress.x = (board.DISPLAY.width - label_progress.bounding_box[2]) // 2
    label_progress.y = base_y
    poem_group.append(label_progress)


def show_poem_ui(group, poem):
    print(poem)
    poem_content = [f'《{poem[K_RHYTHMIC]}》', f'【{poem[K_AUTHOR]}】']
    poem_content.extend(poem[K_PARAGRAPHS])
    poem_content_height = len(poem_content) * (CN_FONT_HEIGHT + CN_FONT_GAP) - CN_FONT_GAP
    poem_content_base_y = (board.DISPLAY.height - (
            poem_content_height + TERMINAL_FONT_HEIGHT) + CN_FONT_HEIGHT) // 2
    start_idx = 0
    start_idx_max = POEM_CONTENT_SHOW_LINES
    long_poem = False
    if len(poem_content) > POEM_CONTENT_SHOW_LINES:
        long_poem = True
        poem_content_base_y = CN_FONT_HEIGHT // 2
        start_idx_max = POEM_CONTENT_SHOW_LINES
    show_poem(group, poem_content, poem[K_AUTHOR], poem_content_base_y, start_idx)
    while True:
        gc.collect()
        key_status = key_status_read()
        need_refresh = False
        if key_status[KEY_RIGHT]:
            print(gc.mem_alloc(), gc.mem_free(), key_status)
            if long_poem and start_idx + 1 + POEM_CONTENT_SHOW_LINES <= len(poem_content):
                start_idx = (start_idx + 1) % start_idx_max
                need_refresh = True
        elif key_status[KEY_LEFT]:
            print(gc.mem_alloc(), gc.mem_free(), key_status)
            if long_poem and start_idx - 1 >= 0:
                start_idx = start_idx - 1
                need_refresh = True
        elif key_status[KEY_MID]:
            print(gc.mem_alloc(), gc.mem_free(), key_status)
            group.pop()
            break
        if need_refresh:
            group.pop()
            show_poem(group, poem_content, poem[K_AUTHOR], poem_content_base_y, start_idx)
        time.sleep(0.05)


def font_load_glyphs(font, text):
    char_set = set([])
    for c in text:
        char_set.add(c)
    font.load_glyphs(''.join(list(char_set)).encode('utf-8'))


UI_DEBUG = False


def main():
    # 创建显示控件组
    group = displayio.Group()
    # 绑定到屏幕
    board.DISPLAY.show(group)
    # 显示背景
    show_display_bg(group)

    # 创建HTTP Session
    http = http_create_session()

    if not UI_DEBUG:
        # WiFi扫描
        show_wifi_scanning(group)
        wifi_start_scanning()
        time.sleep(5)
        gc.collect()

        # WiFi连接热点
        group.pop()  # 弹出之前的显示文字
        show_wifi_connecting(group)
        ssid, psk = wifi_load_config()
        wifi_connect_hostspot(ssid, psk)
        group.pop()  # 弹出之前的显示文字
        show_wifi_status(group)

        # 抓取索引
        ci_index = http_get_ci_index(http)
        group.pop()  # 弹出此前的文字
    else:
        # UI调试模式，直接读文件数据
        ci_index = []
        with open('/song/ci_pop/index') as f:
            index_text = f.read()
            ci_index = json.loads(index_text)
            font_load_glyphs(g_cn_font, index_text)

    while True:
        page_id = 0
        while True:
            # 显示索引列表
            index_group = show_ci_index(group, ci_index, page_id)

            # 绘制选择光标，并进行UI交互
            select_id, key_pressed = show_select_ui(index_group, page_id)

            # 检查选中的条目是否为当前分页的
            page_first = page_id * TOC_CONTENT_LINES
            page_last = page_first + TOC_CONTENT_LINES - 1
            if (page_first <= select_id) and (select_id <= page_last):
                # 是当前分页的，跳转到显示诗词界面
                group.pop()
                gc.collect()
                break

            # 不是当前分页， 更新分页号
            page_id = select_id // TOC_CONTENT_LINES
            group.pop()  # 接下来重新绘制
            gc.collect()

        if not UI_DEBUG:
            # 抓取诗词
            poem = http_get_poem(http, ci_index[select_id][K_PATH])
        else:
            # 读取诗词
            poem = {}
            with open(f'/song/ci_pop/{ci_index[select_id][K_PATH]}') as f:
                poem_text = f.read()
                poem = json.loads(poem_text)
                font_load_glyphs(g_cn_font, poem_text)

        # 显示诗词界面
        show_poem_ui(group, poem)


if __name__ == '__main__':
    main()

